#include <sys/socket.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
#include <errno.h>
#include <netdb.h>

#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#include<memory.h>
#include<dirent.h>

#include "test.h"
#include "utils.h"

#define PORTNUM 8080	//80은 시스템 포트.
			// 80을 사용하게 되면 루트 권한으로
			// 실행해야 한다. 일반유저를 원한다면 다른 포트로.
#define MAXLINE 1024
#define HEADERSIZE 1024 //클라이언트의 요청에 대해 보내주는 http헤더 정보의 크기

#define SERVER "Web-Share-file-system/1.0(dododot)" //서버 프로그램 이름과 버전

static globals *pglobal;
context servers[MAX_OUTPUT_PLUGINS];

cfd lcfd;
char root[MAXLINE];	//웹문서 파일이 존재하는 루트 디렉터리
char lsdir[IO_BUFFER];	//파일들의 이름  

int webserv(int sockfd);
int protocol_parser(char *str, struct user_request *request);
int sendpage(int sockfd, char *filename, char *http_ver, char *codemsg);
void pwdpls();

int main(int argc, char **argv)
{
	int listenfd;
	int clientfd;
	socklen_t clilen;
	int pid;
	int optval = 1;
                

	struct sockaddr_in addr, cliaddr;

	if(argc !=2 )
	{
		printf("Usage : %s [root directory]\n", argv[0]);
		return 1;
	}
	memset(root, 0x00, MAXLINE);
	sprintf(root, "%s", argv[1]);

	if((listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		return 1;
	}

	setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));//SO_REUSEADDR 옵션 - 소캣을 재사용
	memset(&addr, 0x00, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = htonl(INADDR_ANY);
	/*	htonl :: host 시스템에서 network로 short형 데이터를 보낼 때 바이트 오더를 바꿔주는 함수
		ntohl :: htonl과 반대 */
	
	addr.sin_port = htons(PORTNUM);

       /*INADDR_ANY 은 localhost인데 자신의 IP가 아닌 ox0000~으로 되어있다.
	 INADDR_ANY로 주소를 잡아주면 커널(OS)이 그 주소를 잡아주는게 아니라 0.0.0.0으로
	 대기중에 있다가 connect(클라이언트)에서 특정 서버로 접속하면 소켓이 바인드 된다. */

	if(bind(listenfd, (struct sockaddr *)&addr, sizeof(addr)) == -1)
	{
		return 1;
	}
	if(listen(listenfd, 5) == -1)
	{
		return 1;
	}

	signal(SIGCHLD, SIG_IGN);
	while(1)
	{
		clilen = sizeof(clilen);
		clientfd = accept(listenfd , (struct sockaddr *)&cliaddr, &clilen);
		if(clientfd == -1)
		{
			return 1;
		}
		pid = fork();

		if(pid == 0)
		{
			webserv(clientfd);
			//pwdpls();	// 파일들의 이름을 스캔 하는 사용자 함수.
			close(clientfd);
			exit(0);
		}
		if(pid == -1)
		{
			return 1;
		}
		close(clientfd);
	}
}

/////////////////////////////////////
//클라이언트의 요청을 처리하는 함수//
/////////////////////////////////////

int webserv(int sockfd)
{
	char buf[MAXLINE];
	char page[MAXLINE];
	struct user_request request;

	memset(&request, 0x00, sizeof(request));
	memset(buf, 0x00, MAXLINE);
	
	if(read(sockfd, buf, MAXLINE) <= 0)	//클라이언트의 요청을 읽는다.
	{
		return -1;
	}
	protocol_parser(buf, &request);	//클라이언트 요청을 분석한다. 분석한 정보는 user_request에 저장된다. 


	// 파일들의 이름을 스캔 하는 사용자 함수.
			//sendpage(sockfd, NULL, request.http_ver,"404 Error");
			//sendpage(sockfd, NULL, request.http_ver, lsdir);



	if(strcmp(request.method, "GET") == 0)	//get요청이라면, 요청한 페이지를 찾아서 읽은 다음 소켓으로 전송.
	{

		printf("날아온 메시지 ?? '%s' \n", request.page);
		sprintf(page, "%s%s", root, request.page);
		if(access(page, R_OK) != 0)
		{	pwdpls();	// 파일들의 이름을 스캔 하는 사용자 함수.
			//sendpage(sockfd, NULL, request.http_ver,"404 Error");
			sendpage(sockfd, NULL, request.http_ver, lsdir);	//파일이 존재하지 않으면,
		}
		else
		{
			sendpage(sockfd, page, request.http_ver, "200 OK");//읽을 파일이 존재
		}

		if(strcmp(request.page,"/command=test")==0)
		{	
			pwdpls();			
			sprintf(page, "%s%s", root, lsdir);
			//printf("메시지는 날아왔습니다. %s \n", lsdir);
			sendpage(sockfd, page, request.http_ver,lsdir);
			

		}
		//else printf("명령이 잘 안넘어왔어 흑");
	}
	else
	{
			
		sendpage(sockfd, NULL, request.http_ver, "500 Internal Server Error");	//get 이외의 예외요청.
	}

 	
	return 1;
}




/////////////////////////////////////////////////////////////////////////////////////////////////////
//sockfd : 소캣 지정번호,  filename : 읽을 파일 이름, http_ver : HTTP버전,  codemsg : 코드 메시지.///
/////////////////////////////////////////////////////////////////////////////////////////////////////

int sendpage(int sockfd, char *filename, char *http_ver, char *codemsg)
{
	struct tm *tm_ptr;
	time_t the_time;
	struct stat fstat;
	char header[HEADERSIZE];
	int fd; 
	int readn;
	int content_length=0;
	char buf[MAXLINE];
	char date_str[80];
	char *daytable = "Sun\0Mon\0Tue\0Wed\0Thu\0Fri\0Sat\0";
	char *montable = "Jan\0Feb\0Mar\0Apr\0May\0Jun\0Jul\0Aug\0Sep\0Oct\0Nov\0Dec\0";

	memset(header, 0x00, HEADERSIZE);

	time(&the_time);
	tm_ptr = localtime(&the_time);

	sprintf(date_str, "%s, %d %s %d %02d:%02d:%02d GMT",
			daytable+(tm_ptr->tm_wday*4),
			tm_ptr->tm_mday,
			montable+((tm_ptr->tm_mon)*4),
			tm_ptr->tm_year+1900,
			tm_ptr->tm_hour,
			tm_ptr->tm_min,
			tm_ptr->tm_sec
			);

	if(filename != NULL)		//파일을 가져온다. http헤더에서 문서의 크기를 의미하는 content length.
	{   
		stat(filename, &fstat);
		content_length = (int)fstat.st_size;
	}   
	else	
	{   
		content_length = strlen(codemsg);
	}
	sprintf(header, "%s %s\nDate: %s\nServer: %s\nContent-Length: %d\nConnection: close\nContent-Type: text/html; charset=UTF8\n\n",
		http_ver, date_str, codemsg, SERVER, content_length);  //http 헤더를 만든다.
	write(sockfd, header, strlen(header));			//헤더를 보낸다.

	if(filename != NULL)	//파일의 내용을 읽어서 클라이언트로 전송
	{
		fd = open(filename, O_RDONLY);
		memset(buf, 0x00, MAXLINE);
		while((readn = read(fd,buf,MAXLINE)) > 0)
		{
			write(sockfd, buf, readn);
			
		}
		close(fd);
	}
	else
	{
		write(sockfd, codemsg, strlen(codemsg));
	}

	return 1;
}

int protocol_parser(char *str, struct user_request *request)
{
	char *tr;
	char token[] = " \r\n";
	int i;
	tr = strtok(str, token);
	for (i = 0; i < 3; i++)
	{
		if(tr == NULL) break;
		if(i == 0)
			strcpy(request->method, tr);
		else if(i == 1)
			strcpy(request->page, tr);
		else if(i == 2)
			strcpy(request->http_ver, tr);
		tr = strtok(NULL, token);
	}
	return 1;
}

/******************************************************************************
Description.: Read a single line from the provided fildescriptor.
This funtion will return under two conditions:
* line end was reached
* timeout occured
Input Value.: * fd.....: fildescriptor to read from
* iobuf..: iobuffer that allows to use this functions from multiple
threads because the complete context is the iobuffer.
* buffer.: The buffer to store values at, will be set to zero
before storing values.
* len....: the length of buffer
* timeout: seconds to wait for an answer
Return Value: * buffer.: will become filled with bytes read
* iobuf..: May get altered to save the context for future calls.
* func().: bytes copied to buffer or -1 in case of error
******************************************************************************/
/* read just a single line or timeout */

int _readline(int fd, iobuffer *iobuf, void *buffer, size_t len, int timeout)
{
	char c = '\0', *out = buffer;
	int i;

	memset(buffer, 0, len);

	for(i = 0; i < len && c != '\n'; i++) 
	{
		if(_read(fd, iobuf, &c, 1, timeout) <= 0)
		{
			/* timeout or error occured */
			return -1;
		}
		*out++ = c;
	}

	return i;
}


/******************************************************************************
Description.: read with timeout, implemented without using signals
tries to read len bytes and returns if enough bytes were read
or the timeout was triggered. In case of timeout the return
value may differ from the requested bytes "len".
Input Value.: * fd.....: fildescriptor to read from
* iobuf..: iobuffer that allows to use this functions from multiple
threads because the complete context is the iobuffer.
* buffer.: The buffer to store values at, will be set to zero
before storing values.
* len....: the length of buffer
* timeout: seconds to wait for an answer
Return Value: * buffer.: will become filled with bytes read
* iobuf..: May get altered to save the context for future calls.
* func().: bytes copied to buffer or -1 in case of error
******************************************************************************/

int _read(int fd, iobuffer *iobuf, void *buffer, size_t len, int timeout)
{
	int copied = 0, rc, i;
	fd_set fds;
	struct timeval tv;

	memset(buffer, 0, len);

	while((copied < len))
	{
		i = MIN(iobuf->level, len - copied);
		memcpy(buffer + copied, iobuf->buffer + IO_BUFFER - iobuf->level, i);

		iobuf->level -= i;
		copied += i;
		if(copied >= len)
			return copied;

		/* select will return in case of timeout or new data arrived */
		tv.tv_sec = timeout;
		tv.tv_usec = 0;
		FD_ZERO(&fds);
		FD_SET(fd, &fds);
		if((rc = select(fd + 1, &fds, NULL, NULL, &tv)) <= 0)
		{
			if(rc < 0)
				exit(EXIT_FAILURE);

			/* this must be a timeout */
			return copied;
		}

		init_iobuffer(iobuf);

		/*
		* there should be at least one byte, because select signalled it.
		* But: It may happen (very seldomly), that the socket gets closed remotly between
		* the select() and the following read. That is the reason for not relying
		* on reading at least one byte.
		*/
		if((iobuf->level = read(fd, &iobuf->buffer, IO_BUFFER)) <= 0)
		{
			/* an error occured */
			return -1;
		}

		/* align data to the end of the buffer if less than IO_BUFFER bytes were read */
		memmove(iobuf->buffer + (IO_BUFFER - iobuf->level), iobuf->buffer, iobuf->level);
	}

	return 0;
}


/******************************************************************************
Description.: initializes the iobuffer structure properly
Input Value.: pointer to already allocated iobuffer
Return Value: iobuf
******************************************************************************/
void init_iobuffer(iobuffer *iobuf) {
	memset(iobuf->buffer, 0, sizeof(iobuf->buffer));
	iobuf->level = 0;
}


/******************************************************************************
Description.: initializes the request structure properly
Input Value.: pointer to already allocated req
Return Value: req
******************************************************************************/
void init_request(request *req)
{
	req->type        = A_UNKNOWN;
	req->type        = A_UNKNOWN;
	req->parameter   = NULL;
	req->client      = NULL;
	req->credentials = NULL;
}

/******************************************************************************
Description.: If strings were assigned to the different members free them
This will fail if strings are static, so always use strdup().
Input Value.: req: pointer to request structure
Return Value: -
******************************************************************************/
void free_request(request *req)
{
	if(req->parameter != NULL) free(req->parameter);
	if(req->client != NULL) free(req->client);
	if(req->credentials != NULL) free(req->credentials);
}

/******************************************************************************
Description.: Serve a connected TCP-client. This thread function is called
for each connect of a HTTP client like a webbrowser. It determines
if it is a valid HTTP request and dispatches between the different
response options.
Input Value.: arg is the filedescriptor and server-context of the connected TCP
socket. It must have been allocated so it is freeable by this
thread function.
Return Value: always NULL
******************************************************************************/
/* thread for clients that connected to this server */

void *client_thread(void *arg)
{
	int cnt;
	char input_suffixed = 0;
	int input_number = 0;
	char buffer[BUFFER_SIZE] = {0}, *pb = buffer;
	iobuffer iobuf;
	request req;
	cfd lcfd; /* local-connected-file-descriptor */

	/* we really need the fildescriptor and it must be freeable by us */
	if(arg != NULL)
	{
		memcpy(&lcfd, arg, sizeof(cfd));
		free(arg);
	} else
		return NULL;

	/* initializes the structures */
	init_iobuffer(&iobuf);
	init_request(&req);

	/* What does the client want to receive? Read the request. */
	memset(buffer, 0, sizeof(buffer));
	if((cnt = _readline(lcfd.fd, &iobuf, buffer, sizeof(buffer) - 1, 5)) == -1)
	{
		close(lcfd.fd);
		return NULL;
	}

	/* determine what to deliver */
	/*    if(strstr(buffer, "GET /?action=snapshot") != NULL) {
	req.type = A_SNAPSHOT;
	#ifdef WXP_COMPAT
	} else if((strstr(buffer, "GET /cam") != NULL) && (strstr(buffer, ".jpg") != NULL)) {
	req.type = A_SNAPSHOT;
	#endif
	input_suffixed = 255;
	} /*else if(strstr(buffer, "GET /?action=stream") != NULL) {
	input_suffixed = 255;
	req.type = A_STREAM; 		//스트림 같은거 없ㅋ음
	#ifdef WXP_COMPAT
	} else if((strstr(buffer, "GET /cam") != NULL) && (strstr(buffer, ".mjpg") != NULL)) {
	req.type = A_STREAM;
	#endif
	input_suffixed = 255;
	}*/ 

	if(strstr(buffer, "GET /?action=command") != NULL)
	{    //웹에서 커멘드를 받는 부분.
		int len;
		req.type = A_COMMAND;


		/* advance by the length of known string */
		if((pb = strstr(buffer, "GET /?action=command")) == NULL)
		{
			printf("HTTP request seems to be malformed\n");
			send_error(lcfd.fd, 400, "Malformed HTTP request");
			close(lcfd.fd);
			return NULL;
		}

		/* only accept certain characters */
		len = MIN(MAX(strspn(pb, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-=&1234567890%./"), 0), 100);

		req.parameter = malloc(len + 1);
		if(req.parameter == NULL)
		{
			exit(EXIT_FAILURE);
		}
		memset(req.parameter, 0, len + 1);
		strncpy(req.parameter, pb, len);

		if(unescape(req.parameter) == -1)
		{
			free(req.parameter);
			send_error(lcfd.fd, 500, "could not properly unescape command parameter string");
			//LOG("could not properly unescape command parameter string\n");
			close(lcfd.fd);
			return NULL;
		}


		//여기에 웹에서 얻은 명령어를 처리하고 호출하는 함수를 작성합니다.
		if(strcmp(req.parameter, "throttle up") == 0)
                        printf("오정학학학핰핰하아카핰아카");

	}

	else
	{
		int len;

		printf("try to serve a file\n");
		req.type = A_FILE;

		if((pb = strstr(buffer, "GET /")) == NULL)
		{
			printf("HTTP request seems to be malformed\n");
			send_error(lcfd.fd, 400, "Malformed HTTP request");
			close(lcfd.fd);
			return NULL;
		}

		pb += strlen("GET /");
		len = MIN(MAX(strspn(pb, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ._-1234567890"), 0), 100);
		req.parameter = malloc(len + 1);
		if(req.parameter == NULL)
		{
			exit(EXIT_FAILURE);
		}
		memset(req.parameter, 0, len + 1);
		strncpy(req.parameter, pb, len);

		printf("parameter (len: %d): \"%s\"\n", len, req.parameter);
	}

	/*
	* Since when we are working with multiple input plugins
	* there are some url which could have a _[plugin number suffix]
	* For compatibility reasons it could be left in that case the output will be
	* generated from the 0. input plugin
	*/
	if(input_suffixed)
	{
		char *sch = strchr(buffer, '_');
		if(sch != NULL)
		{  // there is an _ in the url so the input number should be present
			//printf("sch %s\n", sch + 1); // FIXME if more than 10 input plugin is added
			char numStr[3];
			memset(numStr, 0, 3);
			strncpy(numStr, sch + 1, 1);
			input_number = atoi(numStr);
		}
		//printf("input plugin_no: %d\n", input_number);
	}

	/*
	* parse the rest of the HTTP-request
	* the end of the request-header is marked by a single, empty line with "\r\n"
	*/
	do {
		memset(buffer, 0, sizeof(buffer));

		if((cnt = _readline(lcfd.fd, &iobuf, buffer, sizeof(buffer) - 1, 5)) == -1)
		{
			free_request(&req);
			close(lcfd.fd);
			return NULL;
		}

		if(strstr(buffer, "User-Agent: ") != NULL)
		{
			req.client = strdup(buffer + strlen("User-Agent: "));
		}
		else if(strstr(buffer, "Authorization: Basic ") != NULL)
		{
			req.credentials = strdup(buffer + strlen("Authorization: Basic "));
			decodeBase64(req.credentials);
			//printf("username:password: %s\n", req.credentials);
		}

	} while(cnt > 2 && !(buffer[0] == '\r' && buffer[1] == '\n'));

	/* check for username and password if parameter -c was given */
	if(lcfd.pc->conf.credentials != NULL) 
	{
		if(req.credentials == NULL || strcmp(lcfd.pc->conf.credentials, req.credentials) != 0)
		{
			//printf("access denied\n");
			send_error(lcfd.fd, 401, "username and password do not match to configuration");
			close(lcfd.fd);
			if(req.parameter != NULL) free(req.parameter);
			if(req.client != NULL) free(req.client);
			if(req.credentials != NULL) free(req.credentials);
			return NULL;
		}
		//printf("access granted\n");
	}

	/* now it's time to answer */

	if(!(input_number < pglobal->incnt))
	{
		//printf("Input number: %d out of range (valid: 0..%d)\n", input_number, pglobal->incnt-1);
		send_error(lcfd.fd, 404, "Invalid input plugin number");
		req.type = A_UNKNOWN;
	}

	switch(req.type)
	{
	case A_COMMAND:
		if(lcfd.pc->conf.nocommands)

		{
			send_error(lcfd.fd, 501, "this server is configured to not accept commands");
		}
		break;

	case A_FILE:
		if(lcfd.pc->conf.www_folder == NULL)
			send_error(lcfd.fd, 501, "no www-folder configured");
		else
			send_file(lcfd.pc->id, lcfd.fd, req.parameter);
		break;
	default:
		printf("unknown request\n");
	}

	close(lcfd.fd);
	free_request(&req);

	printf("leaving HTTP client thread\n");
	return NULL;
}


/******************************************************************************
Description.: This function cleans up ressources allocated by the server_thread
Input Value.: arg is not used
Return Value: -
******************************************************************************/
void server_cleanup(void *arg)
{
	context *pcontext = arg;
	int i;

	//OPRINT("cleaning up ressources allocated by server thread #%02d\n", pcontext->id);
	printf("서버 스레드에 의해 지정된 리소스들을 반환해줍니다.");

	for(i = 0; i < MAX_SD_LEN; i++)
		close(pcontext->sd[i]);
}

/******************************************************************************
Description.: Open a TCP socket and wait for clients to connect. If clients
connect, start a new thread for each accepted connection.
Input Value.: arg is a pointer to the globals struct
Return Value: always NULL, will only return on exit
******************************************************************************/
void *server_thread(void *arg)
{
	int on;
	pthread_t client;
	struct addrinfo *aip, *aip2;
	struct addrinfo hints;
	struct sockaddr_storage client_addr;
	socklen_t addr_len = sizeof(struct sockaddr_storage);
	fd_set selectfds;
	int max_fds = 0;
	char name[NI_MAXHOST];
	int err;
	int i;

	context *pcontext = arg;
	pglobal = pcontext->pglobal;

	/* set cleanup handler to cleanup ressources */
	pthread_cleanup_push(server_cleanup, pcontext);

	bzero(&hints, sizeof(hints));
	hints.ai_family = PF_UNSPEC;
	hints.ai_flags = AI_PASSIVE;
	hints.ai_socktype = SOCK_STREAM;

	snprintf(name, sizeof(name), "%d", ntohs(pcontext->conf.port));
	if((err = getaddrinfo(NULL, name, &hints, &aip)) != 0) 
	{
		perror(gai_strerror(err));
		exit(EXIT_FAILURE);
	}

	for(i = 0; i < MAX_SD_LEN; i++)
		pcontext->sd[i] = -1;

	/* open sockets for server (1 socket / address family) */
	i = 0;
	for(aip2 = aip; aip2 != NULL; aip2 = aip2->ai_next) 
	{
		if((pcontext->sd[i] = socket(aip2->ai_family, aip2->ai_socktype, 0)) < 0)
		{
			continue;
		}

		/* ignore "socket already in use" errors */
		on = 1;
		if(setsockopt(pcontext->sd[i], SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
		{
			perror("setsockopt(SO_REUSEADDR) failed");
		}

		/* IPv6 socket should listen to IPv6 only, otherwise we will get "socket already in use" */
		on = 1;
		if(aip2->ai_family == AF_INET6 && setsockopt(pcontext->sd[i], IPPROTO_IPV6, IPV6_V6ONLY,
			(const void *)&on , sizeof(on)) < 0)
		{
			perror("setsockopt(IPV6_V6ONLY) failed");
		}

		/* perhaps we will use this keep-alive feature oneday */
		/* setsockopt(sd, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on)); */

		if(bind(pcontext->sd[i], aip2->ai_addr, aip2->ai_addrlen) < 0)
		{
			perror("bind");
			pcontext->sd[i] = -1;
			continue;
		}

		if(listen(pcontext->sd[i], 10) < 0) 
		{
			perror("listen");
			pcontext->sd[i] = -1;
		} else
		{
			i++;
			if(i >= MAX_SD_LEN)
			{
				//OPRINT("%s(): maximum number of server sockets exceeded", __FUNCTION__);
				printf("서버 소켓들의 최대 수치가 초과 되었습니다..");
				i--;
				break;
			}
		}
	}

	pcontext->sd_len = i;

	if(pcontext->sd_len < 1)
	{
		//OPRINT("%s(): bind(%d) failed", __FUNCTION__, htons(pcontext->conf.port));
		printf("뭔가 존나 실패했는데 뭔지 모르겠습니다.");
		closelog();
		exit(EXIT_FAILURE);
	}

	/* create a child for every client that connects */
	while(!pglobal->stop)
	{
		//int *pfd = (int *)malloc(sizeof(int));
		cfd *pcfd = malloc(sizeof(cfd));

		if(pcfd == NULL) {
			fprintf(stderr, "failed to allocate (a very small amount of) memory\n");
			exit(EXIT_FAILURE);
		}

		printf("waiting for clients to connect\n");

		do {
			FD_ZERO(&selectfds);

			for(i = 0; i < MAX_SD_LEN; i++)
			{
				if(pcontext->sd[i] != -1)
				{
					FD_SET(pcontext->sd[i], &selectfds);

					if(pcontext->sd[i] > max_fds)
						max_fds = pcontext->sd[i];
				}
			}

			err = select(max_fds + 1, &selectfds, NULL, NULL, NULL);

			if(err < 0 && errno != EINTR)
			{
				perror("select");
				exit(EXIT_FAILURE);
			}
		} while(err <= 0);

		for(i = 0; i < max_fds + 1; i++)
		{
			if(pcontext->sd[i] != -1 && FD_ISSET(pcontext->sd[i], &selectfds))
			{
				pcfd->fd = accept(pcontext->sd[i], (struct sockaddr *)&client_addr, &addr_len);
				pcfd->pc = pcontext;

				/* start new thread that will handle this TCP connected client */
				printf("create thread to handle client that just established a connection\n");

				if(pthread_create(&client, NULL, &client_thread, pcfd) != 0)
				{
					printf("could not launch another client thread\n");
					close(pcfd->fd);
					free(pcfd);
					continue;
				}
				pthread_detach(client);
			}
		}
	}

	printf("leaving server thread, calling cleanup function now\n");
	pthread_cleanup_pop(1);

	return NULL;
}


/******************************************************************************
Description.: Send HTTP header and copy the content of a file. To keep things
simple, just a single folder gets searched for the file. Just
files with known extension and supported mimetype get served.
If no parameter was given, the file "index.html" will be copied.
Input Value.: * fd.......: filedescriptor to send data to
* parameter: string that consists of the filename
* id.......: specifies which server-context is the right one
Return Value: -
******************************************************************************/
void send_file(int id, int fd, char *parameter)
{
	char buffer[BUFFER_SIZE] = {0};
	char *extension, *mimetype = NULL;
	int i, lfd;
	config conf = servers[id].conf;

	/* in case no parameter was given */
	if(parameter == NULL || strlen(parameter) == 0)
		parameter = "index.html";

	/* find file-extension */
	char * pch;
	pch = strchr(parameter, '.');
	int lastDot = 0;
	while(pch != NULL) {
		lastDot = pch - parameter;
		pch = strchr(pch + 1, '.');
	}

	if(lastDot == 0) {
		send_error(fd, 400, "No file extension found");
		return;
	} else {
		extension = parameter + lastDot;
		printf("%s EXTENSION: %s\n", parameter, extension);
	}

	/* determine mime-type */
	for(i = 0; i < LENGTH_OF(mimetypes); i++)
	{
		if(strcmp(mimetypes[i].dot_extension, extension) == 0)
		{
			mimetype = (char *)mimetypes[i].mimetype;
			break;
		}
	}

	/* in case of unknown mimetype or extension leave */
	if(mimetype == NULL)
	{
		send_error(fd, 404, "MIME-TYPE not known");
		return;
	}

	/* now filename, mimetype and extension are known */
	printf("trying to serve file \"%s\", extension: \"%s\" mime: \"%s\"\n", parameter, extension, mimetype);

	/* build the absolute path to the file */
	strncat(buffer, conf.www_folder, sizeof(buffer) - 1);
	strncat(buffer, parameter, sizeof(buffer) - strlen(buffer) - 1);

	/* try to open that file */
	if((lfd = open(buffer, O_RDONLY)) < 0)
	{
		printf("file %s not accessible\n", buffer);
		send_error(fd, 404, "Could not open file");
		return;
	}
	printf("opened file: %s\n", buffer);

	/* prepare HTTP header */
	sprintf(buffer, "HTTP/1.0 200 OK\r\n" \
		"Content-type: %s\r\n" \
		"\r\n", mimetype);
	i = strlen(buffer);

	/* first transmit HTTP-header, afterwards transmit content of file */
	do {
		if(write(fd, buffer, i) < 0) 
		{
			close(lfd);
			return;
		}
	} while((i = read(lfd, buffer, sizeof(buffer))) > 0);

	/* close file, job done */
	close(lfd);
}



/******************************************************************************
Description.: Send error messages and headers.
Input Value.: * fd.....: is the filedescriptor to send the message to
* which..: HTTP error code, most popular is 404
* message: append this string to the displayed response
Return Value: -
******************************************************************************/
void send_error(int fd, int which, char *message)
{
	char buffer[BUFFER_SIZE] = {0};

	if(which == 401) {
		sprintf(buffer, "HTTP/1.0 401 Unauthorized\r\n" \
			"Content-type: text/plain\r\n" \
			"WWW-Authenticate: Basic realm=\"MJPG-Streamer\"\r\n" \
			"\r\n" \
			"401: Not Authenticated!\r\n" \
			"%s", message);
	} else if(which == 404) {
		sprintf(buffer, "HTTP/1.0 404 Not Found\r\n" \
			"Content-type: text/plain\r\n" \
			"\r\n" \
			"404: Not Found!\r\n" \
			"%s", message);
	} else if(which == 500) {
		sprintf(buffer, "HTTP/1.0 500 Internal Server Error\r\n" \
			"Content-type: text/plain\r\n" \
			"\r\n" \
			"500: Internal Server Error!\r\n" \
			"%s", message);
	} else if(which == 400) {
		sprintf(buffer, "HTTP/1.0 400 Bad Request\r\n" \
			"Content-type: text/plain\r\n" \
			"\r\n" \
			"400: Not Found!\r\n" \
			"%s", message);
	} else {
		sprintf(buffer, "HTTP/1.0 501 Not Implemented\r\n" \
			"Content-type: text/plain\r\n" \
			"\r\n" \
			"501: Not Implemented!\r\n" \
			"%s", message);
	}

	if(write(fd, buffer, strlen(buffer)) < 0)
	{
		printf("write failed, done anyway\n");
	}
}

/******************************************************************************
Description.: replace %XX with the character code it represents, URI
Input Value.: string to unescape
Return Value: 0 if everything is ok, -1 in case of error
******************************************************************************/
int unescape(char *string)
{
    char *source = string, *destination = string;
    int src, dst, length = strlen(string), rc;

    /* iterate over the string */
    for(dst = 0, src = 0; src < length; src++) {

        /* is it an escape character? */
        if(source[src] != '%') {
            /* no, so just go to the next character */
            destination[dst] = source[src];
            dst++;
            continue;
        }

        /* yes, it is an escaped character */

        /* check if there are enough characters */
        if(src + 2 > length) {
            return -1;
            break;
        }

        /* perform replacement of %## with the corresponding character */
        if((rc = hex_char_to_int(source[src+1])) == -1) return -1;
        destination[dst] = rc * 16;
        if((rc = hex_char_to_int(source[src+2])) == -1) return -1;
        destination[dst] += rc;

        /* advance pointers, here is the reason why the resulting string is shorter */
        dst++; src += 2;
    }

    /* ensure the string is properly finished with a null-character */
    destination[dst] = '\0';

    return 0;
}


/******************************************************************************
Description.: Decodes the data and stores the result to the same buffer.
              The buffer will be large enough, because base64 requires more
              space then plain text.
Hints.......: taken from busybox, but it is GPL code
Input Value.: base64 encoded data
Return Value: plain decoded data
******************************************************************************/
void decodeBase64(char *data)
{
    const unsigned char *in = (const unsigned char *)data;
    /* The decoded size will be at most 3/4 the size of the encoded */
    unsigned ch = 0;
    int i = 0;

    while(*in) {
        int t = *in++;

        if(t >= '0' && t <= '9')
            t = t - '0' + 52;
        else if(t >= 'A' && t <= 'Z')
            t = t - 'A';
        else if(t >= 'a' && t <= 'z')
            t = t - 'a' + 26;
        else if(t == '+')
            t = 62;
        else if(t == '/')
            t = 63;
        else if(t == '=')
            t = 0;
        else
            continue;

        ch = (ch << 6) | t;
        i++;
        if(i == 4) {
            *data++ = (char)(ch >> 16);
            *data++ = (char)(ch >> 8);
            *data++ = (char) ch;
            i = 0;
        }
    }
    *data = '\0';
}

/******************************************************************************
Description.: convert a hexadecimal ASCII character to integer
Input Value.: ASCII character
Return Value: corresponding value between 0 and 15, or -1 in case of error
******************************************************************************/
int hex_char_to_int(char in)
{
    if(in >= '0' && in <= '9')
        return in - '0';

    if(in >= 'a' && in <= 'f')
        return (in - 'a') + 10;

    if(in >= 'A' && in <= 'F')
        return (in - 'A') + 10;

    return -1;
}

void pwdpls()
{

        DIR *pdir;
        struct dirent *pde;
        int i=0;
        char buf[IO_BUFFER];
        
        memset(buf, 0, IO_BUFFER);
        if(chdir("/home/web-share-file-system/www/files/")<0)       //위치 지정
        {
                perror("chdir");
                exit(1);
        }

        if(getcwd(buf, IO_BUFFER)<0)      //buf 경로에 저장 
        {
                perror("getcwd");
                exit(1);
        }
       
        if((pdir=opendir(buf))<0)
        {
                perror("opendir");
                exit(1);
        }

        while((pde=readdir(pdir))!=NULL)
        {
                if(strcmp(pde->d_name,"..")==0||strcmp(pde->d_name,".")==0)
			continue;

                if(i==0)
                {
                        sprintf(lsdir,"%s",pde->d_name);
                }
                else
                {
                        sprintf(lsdir,"%s%s%s",lsdir,"|",pde->d_name);
                }

		i++;
        }
        closedir(pdir);

	//printf("결과 : %s\n",lsdir);

	
}